#   Copyright (C) 2008      Tri Tech Information Systems Inc.
# 
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
# 
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
# 
#   You should have received a copy of the GNU General Public License along
#   with this program; if not, write to the Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#   
import os.path

from pyxx import QStringList

from pyxx.formatters import *
from pyxx.parser import CodeModel
from pyxx.wrapper import Wrapper
from pyxx.error import WrapperError, SymbolNotFound
from pyxx.enum import EnumWrapper
from pyxx.function import FunctionWrapper
from pyxx.klass import ClassWrapper
from pyxx.namespace import NamespaceWrapper
from pyxx.interface import InterfaceWrapper
from pyxx.exception import ExceptionWrapper
from pyxx.docstring import DocstringSource

def find_function(fm, model, cpp_name):
    name_path = cpp_name.split('::')
    function_name = name_path[-1]
    parent_name = name_path[:-1]

    if parent_name:
        string_list = QStringList(parent_name)
        parent_model = model.findItem(string_list, fm)
    else:
        parent_model = fm

    try:
        return parent_model.functionMap[function_name]
    except KeyError:
        raise WrapperError("Could not find %s" % cpp_name)


                    



class WrapperContext( Wrapper ):
    TEMPLATE = '''
// This file is automatically generated, DO NOT EDIT

#include <boost/python.hpp>

#include <pyxx/arg.h>
#include <pyxx/enum.h>
#include <pyxx/exception.h>
#include <pyxx/function.h>
#include <pyxx/interface.h>
#include <pyxx/pointer.h>
#include <pyxx/python_gil.h>
#include <pyxx/template.h>
#include <pyxx/util.h>
#include <pyxx/container.h>

#include <iostream>
%(includes)s


using namespace boost::python;
namespace bpy = boost::python;

%(declarations)s
'''
    def __init__( self, parent_context = None ):
        self.wrapper_ctx = self
        Wrapper.__init__( self, None )
        self.klasses = {}
        self.namespaces = {}
        self.enums = {}
        self.functions = []
        self.module = None
        self.includes = []
        self.include_paths = []
        self.parent_context = parent_context
        self.subcontexts = []

        self.all_classes = {}

        self.api = dict([ (k, getattr(self,k)) for k in WrapperContext.__dict__ if k[0] != '_' ])

    def evaluate(self, src):
        self.filename = src
        f = file( src, 'r' )
        exec f in self.api
        f.close()

        self.prepare()

    def Import(self, filename):
        filename = os.path.join(os.path.dirname(self.filename), filename)
        subcontext = WrapperContext(self)
        for path in self.include_paths:
            subcontext.AddIncludePath(path)
        self.subcontexts.append(subcontext)

        subcontext.evaluate(filename)

    def Include(self, filename):
        filename = os.path.join(os.path.dirname(self.filename), filename)
        f = file( filename, 'r' )
        exec f in self.api
        f.close()




    def AddIncludePath( self, path ):
        self.include_paths.append( path )

    def ParseHeaderFile( self, file ):
        self.header_filename = file
        self.AddIncludedFile( file )

        self.cm = CodeModel()
        self.fm = None
        for include_path in self.include_paths:
            joined_path = os.path.join(include_path, file)
            joined_path = os.path.normpath(joined_path)
            if os.path.exists(joined_path):
                self.fm = self.cm.parseFile( joined_path, self.include_paths )
                break

        self.docstring_source = DocstringSource(self.cm)

        if self.fm is None:
            raise WrapperError("Could not find %s" % file)                


    def CreateFunctionWrapper(self, fnname, **kw):
        funs = find_function(self.fm, self.cm, fnname)
        wrap = FunctionWrapper(self, funs, module = self.module, **kw)
        self.functions.append( wrap )
        return wrap

    def register_class(self, wrapper):
        if self.parent_context:
            self.parent_context.register_class(wrapper)

        self.all_classes[ tuple(wrapper.item.qualifiedName) ] = wrapper

    def ExternalWrapper(self, classname):
        if self.parent_context:
            self.parent_context.ExternalWrapper(classname)
        fullname = tuple(classname.split('::'))
        self.all_classes[fullname] = None


    def CreateNamespaceWrapper(self, namespacename, **kw):
        nsmodel = self.cm.findItem( QStringList(namespacename.split('::')), self.fm )
        ns = NamespaceWrapper(self, namespacename, nsmodel, module = self.module, **kw)
        self.namespaces[namespacename] = ns
        return ns


    def CreateClassWrapper( self, classname, **kw ):
        classmodel = self.cm.findItem(QStringList(classname.split('::')), self.fm)
        if classmodel is None:
            raise SymbolNotFound(self, "Unable to locate %s" % classname)

        klass = ClassWrapper( self, classmodel, module = self.module, **kw )
        self.register_class(klass)

        self.klasses[classname] = klass
        return klass

    def CreateInterfaceWrapper( self, classname, **kw ):
        classmodel = self.cm.findItem(QStringList(classname.split('::')), self.fm)
        if classmodel is None:
            raise WrapperError, "Unable to locate %s" % classname

        klass = InterfaceWrapper( self, classmodel, module = self.module, **kw )
        self.register_class(klass)
        self.klasses[classname] = klass
        return klass


    def CreateExceptionWrapper( self, classname, **kw ):
        classmodel = self.cm.findItem(QStringList(classname.split('::')), self.fm)
        if classmodel is None:
            raise WrapperError, "Unable to locate %s" % classname

        klass = ExceptionWrapper( self, classmodel, module = self.module, **kw )
        self.register_class(klass)
        self.klasses[classname] = klass
        return klass


    def CreateEnumWrapper( self, enumname, **kw):
        enummodel = self.cm.findItem(QStringList(enumname.split('::')), self.fm)
        if enummodel is None:
            raise WrapperError, "Unable to locate %s" % enumname

        enum = EnumWrapper(self, enummodel, module = self.module, **kw)
        self.enums[enumname] = enum
        return enum
                



    def AddIncludedFile( self, file ):
        if self.parent_context:
            self.parent_context.AddIncludedFile(file)

        self.includes.append( file )

    def generate_includes( self ):
        return [ '#include "%s"' % f for f in self.includes ]

    @formatter( indent )
    def generate_declarations( self ):
        items = self.namespaces.values() + self.enums.values() + self.klasses.values() + self.functions
        return [ k.generate() for k in items ]

    def do_prepare(self):
        for item in self.klasses.values():
            item.prepare()
        for item in self.functions:
            item.prepare()





